#ifndef U2Exception_H
#define U2Exception_H

#include "U2PreRequest.h"
#include <exception>



// RELEASE_EXCEPTIONS mode
#if U2_ASSERT_MODE == 1
#   ifdef _DEBUG
#       define U2Assert( a, b )       assert( (a) && (b) )
#   else
#       if U2_COMP != U2_COMPILER_BORL
#           define U2Assert( a, b )   if( !(a) ) U2_EXCEPT(U2EG_NAMESPACE_NAME::U2Exception::ERR_RT_ASSERTION_FAILED, (b), "no function info")
#       else
#           define U2Assert( a, b )   if( !(a) ) U2_EXCEPT(U2EG_NAMESPACE_NAME::U2Exception::ERR_RT_ASSERTION_FAILED, (b), __FUNC__ )
#       endif
#   endif
// EXCEPTIONS mode
#elif U2_ASSERT_MODE == 2
#   if U2_COMP != U2_COMPILER_BORL
#       define U2Assert( a, b )       if( !(a) ) U2_EXCEPT(U2EG_NAMESPACE_NAME::U2Exception::ERR_RT_ASSERTION_FAILED, (b), "no function info")
#   else
#       define U2Assert( a, b )       if( !(a) ) U2_EXCEPT(U2EG_NAMESPACE_NAME::U2Exception::ERR_RT_ASSERTION_FAILED, (b), __FUNC__ )
#   endif
// STANDARD mode
#else
#   define U2Assert( a, b )           assert( (a) && (b) )
#endif



U2EG_NAMESPACE_BEGIN


class _U2Share U2Exception : public std::exception
{
protected:
    long                line;
    int                 number;
	U2String            typeName;
    U2String            description;
    U2String            source;
    U2String            file;
	mutable U2String    fullDesc;

public:
    /** Static definitions of error codes.
    @todo
        Add many more exception codes, since we want the user to be able
        to catch most of them.
    */
    enum ExceptionCodes
    {
        ERR_CANNOT_WRITE_TO_FILE,
        ERR_INVALID_STATE,
        ERR_INVALIDPARAMS,
        ERR_RENDERINGAPI_ERROR,
        ERR_DUPLICATE_ITEM,
        ERR_ITEM_NOT_FOUND,
        ERR_FILE_NOT_FOUND,
        ERR_INTERNAL_ERROR,
        ERR_RT_ASSERTION_FAILED, 
		ERR_NOT_IMPLEMENTED,
        ERR_NETWORK_ERROR,
    };

    /** Default constructor.
    */
    U2Exception(int number, const U2String& description, const U2String& source );

    /** Advanced constructor.
    */
    U2Exception(int number, const U2String& description, const U2String& source, const char* type, const char* file, long line );

    /** Copy constructor.
    */
    U2Exception(const U2Exception& rhs);

	/// Needed for  compatibility with std::exception
	~U2Exception() throw() {}

    /** Assignment operator.
    */
    void operator = (const U2Exception& rhs);

    /** Returns a string with the full description of this error.
    @remarks
        The description contains the error number, the description
        supplied by the thrower, what routine threw the exception,
        and will also supply extra platform-specific information
        where applicable. For example - in the case of a rendering
        library error, the description of the error will include both
        the place in which U2 found the problem, and a text
        description from the 3D rendering library, if available.
    */
    virtual const U2String& getFullDescription(void) const;

    /** Gets the error code.
    */
    virtual int getNumber(void) const throw();

    /** Gets the source function.
    */
    virtual const U2String &getSource() const { return source; }

    /** Gets source file name.
    */
    virtual const U2String &getFile() const { return file; }

    /** Gets line number.
    */
    virtual long getLine() const { return line; }

	/** Returns a string with only the 'description' field of this exception. Use 
		getFullDescriptionto get a full description of the error including line number,
		error number and what function threw the exception.
    */
	virtual const U2String &getDescription(void) const { return description; }

	/// Override std::exception::what
	const char* what() const throw() { return getFullDescription().c_str(); }
    
};


/** Template struct which creates a distinct type for each exception code.
@note
This is useful because it allows us to create an overloaded method
for returning different exception types by value without ambiguity. 
From 'Modern C++ Design' (Alexandrescu 2001).
*/
template <int num>
struct U2ExceptionCodeType
{
	enum { number = num };
};

// Specialised exceptions allowing each to be caught specifically
// backwards-compatible since exception codes still used

class _U2Share UnimplementedException : public U2Exception 
{
public:
	UnimplementedException(int inNumber, const U2String& inDescription, const U2String& inSource, const char* inFile, long inLine)
		: U2Exception(inNumber, inDescription, inSource, "UnimplementedException", inFile, inLine) {}
};
class _U2Share FileNotFoundException : public U2Exception
{
public:
	FileNotFoundException(int inNumber, const U2String& inDescription, const U2String& inSource, const char* inFile, long inLine)
		: U2Exception(inNumber, inDescription, inSource, "FileNotFoundException", inFile, inLine) {}
};
class _U2Share IOException : public U2Exception
{
public:
	IOException(int inNumber, const U2String& inDescription, const U2String& inSource, const char* inFile, long inLine)
		: U2Exception(inNumber, inDescription, inSource, "IOException", inFile, inLine) {}
};
class _U2Share InvalidStateException : public U2Exception
{
public:
	InvalidStateException(int inNumber, const U2String& inDescription, const U2String& inSource, const char* inFile, long inLine)
		: U2Exception(inNumber, inDescription, inSource, "InvalidStateException", inFile, inLine) {}
};
class _U2Share InvalidParametersException : public U2Exception
{
public:
	InvalidParametersException(int inNumber, const U2String& inDescription, const U2String& inSource, const char* inFile, long inLine)
		: U2Exception(inNumber, inDescription, inSource, "InvalidParametersException", inFile, inLine) {}
};
class _U2Share ItemIdentityException : public U2Exception
{
public:
	ItemIdentityException(int inNumber, const U2String& inDescription, const U2String& inSource, const char* inFile, long inLine)
		: U2Exception(inNumber, inDescription, inSource, "ItemIdentityException", inFile, inLine) {}
};
class _U2Share InternalErrorException : public U2Exception
{
public:
	InternalErrorException(int inNumber, const U2String& inDescription, const U2String& inSource, const char* inFile, long inLine)
		: U2Exception(inNumber, inDescription, inSource, "InternalErrorException", inFile, inLine) {}
};
class _U2Share RenderingAPIException : public U2Exception
{
public:
	RenderingAPIException(int inNumber, const U2String& inDescription, const U2String& inSource, const char* inFile, long inLine)
		: U2Exception(inNumber, inDescription, inSource, "RenderingAPIException", inFile, inLine) {}
};
class _U2Share RuntimeAssertionException : public U2Exception
{
public:
	RuntimeAssertionException(int inNumber, const U2String& inDescription, const U2String& inSource, const char* inFile, long inLine)
		: U2Exception(inNumber, inDescription, inSource, "RuntimeAssertionException", inFile, inLine) {}
};
class _U2Share NetworkErrorException : public U2Exception
{
public:
    NetworkErrorException(int inNumber, const U2String& inDescription, const U2String& inSource, const char* inFile, long inLine)
        : U2Exception(inNumber, inDescription, inSource, "NetworkErrorException", inFile, inLine) {}
};


/** Class implementing dispatch methods in order to construct by-value
	exceptions of a derived type based just on an exception code.
@remarks
	This nicely handles construction of derived Exceptions by value (needed
	for throwing) without suffering from ambiguity - each code is turned into
	a distinct type so that methods can be overloaded. This allows U2_EXCEPT
	to stay small in implementation (desirable since it is embedded) whilst
	still performing rich code-to-type mapping. 
*/
class U2ExceptionFactory
{
private:
	/// Private constructor, no construction
	U2ExceptionFactory() {}
public:
	static UnimplementedException create(
		U2ExceptionCodeType<U2Exception::ERR_NOT_IMPLEMENTED> code, 
		const U2String& desc, 
		const U2String& src, const char* file, long line)
	{
		return UnimplementedException(code.number, desc, src, file, line);
	}
	static FileNotFoundException create(
		U2ExceptionCodeType<U2Exception::ERR_FILE_NOT_FOUND> code, 
		const U2String& desc, 
		const U2String& src, const char* file, long line)
	{
		return FileNotFoundException(code.number, desc, src, file, line);
	}
	static IOException create(
		U2ExceptionCodeType<U2Exception::ERR_CANNOT_WRITE_TO_FILE> code, 
		const U2String& desc, 
		const U2String& src, const char* file, long line)
	{
		return IOException(code.number, desc, src, file, line);
	}
	static InvalidStateException create(
		U2ExceptionCodeType<U2Exception::ERR_INVALID_STATE> code, 
		const U2String& desc, 
		const U2String& src, const char* file, long line)
	{
		return InvalidStateException(code.number, desc, src, file, line);
	}
	static InvalidParametersException create(
		U2ExceptionCodeType<U2Exception::ERR_INVALIDPARAMS> code, 
		const U2String& desc, 
		const U2String& src, const char* file, long line)
	{
		return InvalidParametersException(code.number, desc, src, file, line);
	}
	static ItemIdentityException create(
		U2ExceptionCodeType<U2Exception::ERR_ITEM_NOT_FOUND> code, 
		const U2String& desc, 
		const U2String& src, const char* file, long line)
	{
		return ItemIdentityException(code.number, desc, src, file, line);
	}
	static ItemIdentityException create(
		U2ExceptionCodeType<U2Exception::ERR_DUPLICATE_ITEM> code, 
		const U2String& desc, 
		const U2String& src, const char* file, long line)
	{
		return ItemIdentityException(code.number, desc, src, file, line);
	}
	static InternalErrorException create(
		U2ExceptionCodeType<U2Exception::ERR_INTERNAL_ERROR> code, 
		const U2String& desc, 
		const U2String& src, const char* file, long line)
	{
		return InternalErrorException(code.number, desc, src, file, line);
	}
	static RenderingAPIException create(
		U2ExceptionCodeType<U2Exception::ERR_RENDERINGAPI_ERROR> code, 
		const U2String& desc, 
		const U2String& src, const char* file, long line)
	{
		return RenderingAPIException(code.number, desc, src, file, line);
	}
	static RuntimeAssertionException create(
		U2ExceptionCodeType<U2Exception::ERR_RT_ASSERTION_FAILED> code, 
		const U2String& desc, 
		const U2String& src, const char* file, long line)
	{
		return RuntimeAssertionException(code.number, desc, src, file, line);
	}
    static NetworkErrorException create(
        U2ExceptionCodeType<U2Exception::ERR_NETWORK_ERROR> code, 
        const U2String& desc, 
        const U2String& src, const char* mFile, long line)
    {
        return NetworkErrorException(code.number, desc, src, mFile, line);
    }
};


#ifndef U2_EXCEPT
#define U2_EXCEPT(num, desc, src)		U2_RETHROW U2ExceptionFactory::create( \
    U2ExceptionCodeType<num>(), desc, src, __FILE__, __LINE__ );
#endif


U2EG_NAMESPACE_END


#endif